#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "znet.h"
#include "tunnel.h"


typedef struct {
    uint8_t version;
    uint8_t cmd;
    uint32_t channel;
} req_t;

typedef struct {
    uint8_t cmd;
} res_t;

#define REQ_SIZE    sizeof(req_t)
#define RES_SIZE    sizeof(res_t)

static inline int send_req(int sock, uint8_t cmd, uint32_t channel)
{
    req_t req = {0};
    req.version = TUNNEL_VERSION;
    req.cmd = cmd;
    req.channel = channel;
    return znet_send_all(sock, &req, REQ_SIZE);
}

static inline int send_res(int sock)
{
    res_t res = {0};
    res.cmd = TUNNEL_CMD_GO;
    return znet_send_all(sock, &res, RES_SIZE);
}

/*
 * RETURN
 * 0: got request
 * < 0: error
 * */
static inline int recv_req(int sock, uint8_t *cmd, uint32_t *channel)
{
    req_t req = {0};
    int ret = 0;
    ret = znet_recv_all(sock, &req, REQ_SIZE);
    if (ret <= 0)
        return -1;
    if (req.version != TUNNEL_VERSION)
        return -2;
    if (req.cmd != TUNNEL_CMD_TX && req.cmd != TUNNEL_CMD_RX)
        return -3;
    *cmd = req.cmd;
    *channel = req.channel;
    return 0;
}


static inline int recv_res(int sock)
{
    res_t res = {0};
    int ret = 0;
    ret = znet_recv_all(sock, &res, RES_SIZE);
    if (ret <= 0) {
        return -1;
    }
    if (res.cmd == TUNNEL_CMD_GO) {
        return 0;
    }
    return -TUNNEL_CMD_GO;
}

int tunnel_tx(int sock, uint32_t channel)
{
    if (send_req(sock, TUNNEL_CMD_TX, channel) <= 0)
        return -1;
    if (recv_res(sock) < 0)
        return -2;
    return 0;
}

int tunnel_rx(int sock, uint32_t channel)
{
    if (send_req(sock, TUNNEL_CMD_RX, channel) <= 0)
        return -1;
    if (recv_res(sock) < 0)
        return -2;
    return 0;
}

int tunnel_go(int sock)
{
    return send_res(sock);
}

/*
 * RETURN
 * 0: got request
 * -: error
 * */
int tunnel_get_req(int sock, uint8_t *cmd, uint32_t *channel)
{
    return recv_req(sock, cmd, channel);
}

